在上一篇當中我們暫時脫離了瀏覽器跟前端,進入到 Node.js 的領域了。在這一篇裡面我們把主題拉回來前端。之前有介紹過 SCSS 跟 Babel,可以把你的 SCSS 還有 ES6 程式碼轉化成瀏覽器看得懂的格式,就可以正常使用了。
在基本功能都正常以後,下一步就是要來優化了。怎麼優化呢?你想想看你的 CSS 跟 JavaScript 是不是有很多可以優化的地方,例如說檔案大小。你可以把所有的空行跟多餘的空白都拿掉,檔案大小就會變小。但程式碼的可讀性也會變得很差,所以記得是要寫到一個新的檔案去,而不是直接更改原來的 source code。
就讓我們先來試試看這個流程吧!我們從無到有把一整個專案 build 起來試試看。
HTML 的話,就讓我們拿之前那個寫簡單的 blog 的 HTML 來用:
<html>
<head>
<link rel="stylesheet" type="text/css" href="cool.css">
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="my.js"></script>
</head>
<body>
<h1 id="title">This is my blog</h1>
<div>
文章標題:<input type="text" name="title" />
</div>
<button id="btn">新增</button>
<div class="posts">
</div>
</body>
</html>
再來的話,用最新最潮的 ES6 語法寫一個 my-es6.js
:
let count = 1;
$(document).ready(() => {
$(document).on('click', '.delete', function() {
$(this).parent().remove();
})
$('#btn').click(() => {
let title = $('input[name=title]').val();
if(title == '') {
alert('標題不能空白');
return;
}
$(`<div class="post">
<h2>${title}</h2>
<p>這是我的第${count++}篇文章</p>
<button class="delete">刪除</button>
</div>`).appendTo('.posts').hide().fadeIn(2000);
})
})
因為要用 babel,所以記得要把環境先弄好:
npm init
npm install babel-cli --save
npm install babel-preset-latest --save-dev
echo '{"presets": ["latest"]}' > .babelrc
然後來改一下package.json
{
"name": "hello",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "babel my-es6.js > my.js"
},
"author": "",
"license": "ISC",
"dependencies": {
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-preset-latest": "^6.16.0"
},
"description": ""
}
再來只要npm run build
,就會把你的 ES6 compile 然後存成 my.js
了。JavaScript 搞定了,接下來來看 CSS 好了,我們一樣照抄之前就寫好的 SCSS:
$title-bg: #c9dacd;
$title-border: #71949e;
$post-bg: #cce8de;
body {
font-family: '微軟正黑體';
}
#title {
text-align: center;
font-size: 40px;
border: 20px inset $title-border;
background: $title-bg;
padding: 15px;
}
.post {
text-align: center;
background: $post-bg;
padding: 30px;
padding-top: 10px;
margin-bottom: 15px;
margin-left: 20px;
margin-right: 20px;
border-radius: 10px;
h2 {
font-size: 30px;
color: #333;
}
p {
margin: 0 auto;
font-size: 16px;
width: 50%;
line-height: 18px;
letter-spacing: 1px;
}
}
還記得怎麼 compile 嗎?
sass cool.scss cool.css
CSS 也完成了。於是我們現在該有的檔案都有了,就只剩下壓縮而已。可以直接找線上的工具來幫我們完成這件事:http://www.minifier.org/
CSS 會變成這樣:
@charset "UTF-8";body{font-family:'微軟正黑體'}#title{text-align:center;font-size:40px;border:20px inset #71949e;background:#c9dacd;padding:15px}.post{text-align:center;background:#cce8de;padding:30px;padding-top:10px;margin-bottom:15px;margin-left:20px;margin-right:20px;border-radius:10px}.post h2{font-size:30px;color:#333}.post p{margin:0 auto;font-size:16px;width:50%;line-height:18px;letter-spacing:1px}
JavaScript 會變成這樣:
'use strict';var count=1;$(document).ready(function(){$(document).on('click','.delete',function(){$(this).parent().remove()});$('#btn').click(function(){var title=$('input[name=title]').val();if(title==''){alert('標題不能空白');return}
$('<div class="post">\n <h2>'+title+'</h2>\n <p>\u9019\u662F\u6211\u7684\u7B2C'+count++ +'\u7BC7\u6587\u7AE0</p>\n <button class="delete">\u522A\u9664</button>\n </div>').appendTo('.posts').hide().fadeIn(2000)})})
空白跟換行基本上都不見了,也因此少掉很多檔案大小。
做到這一步,你就有了檔案很小的 CSS 與 JavaScript 了!但是因為我們的 code 本來就不多,所以能壓縮的幅度也相對的少很多,但是在你真的寫專案的時候,放到 production 上的 code 一定會壓縮,因為檔案大小會少很多很多。
你看看 jQuery 1.12.4,壓縮前是 293 kb,壓縮後是 97 kb,節省了大概 200kb,假設你的網站一天有 1000 次造訪,就節省了大概 200 MB 的流量!這樣累積下來,能夠節省的量十分可觀。而且壓縮過後的檔案傳輸會比較快,所以上線到正式環境的檔案幾乎都會先做過壓縮。
可是現在問題來了,假如說我要改我的 JavaScript 或是 SCSS,改完之後我不就要全部流程再重新做一遍嗎? 如果每一次都要手動 compile,不是有很多指令要打嗎?這樣會不會太麻煩了一點?有沒有什麼更方便的方法?
當然,我可以自己寫 bash script 或是用 node 自己寫一個小程式來跑這些流程,可是如果每一次多一個專案都要再改這些程式碼,這又是另外一件很麻煩的事情了。有沒有什麼標準讓我們可以很方便的處理這些東西呢?有!它就叫做Gulp,slogan 是Automate and enhance your workflow
。
先來安裝一下 gulp 以及等等會使用到的 gulp plguin
npm install --save-dev gulp
npm install --save-dev gulp-babel
接著建立一個 gulpfile.js
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task('default', function() {
gulp.src('my-es6.js')
.pipe(babel())
.pipe(gulp.dest('dist'))
});
然後改一下你的 package.json
的其中一部分
"scripts": {
"build": "gulp"
},
最後執行熟悉的 npm run build
,結束之後你就會看到 dist
資料夾底下多了一個叫做 my-es6.js
的檔案,就是 compile 後的 JavaScript。
好,我們來解釋一下 gulp 到底是幹嘛的,以及到底發生了什麼事情。gulp 它定好了一系列的規則,然後也提供了很多的 API,目的都是讓你能「有系統性、有流程的」去處理很多事情。那他之所以強大就是因為規則都定好了,所以你可以自己幫他寫 plguin,去做你想要做的事情。
以剛剛示範的例子來說,gulp-babel
就是別人寫好的插件,你只要照著用就可以達成跟babel my-es6.js
一樣的效果。gulp.task
就是定義一個任務,後面接任務的名稱跟你要做的事情(一個 function),default
代表預設就會執行到這一個,你要改成其他的也可以。
gulp.src('my-es6.js')
你可以想成是把這個檔案給讀進來,然後通過.pipe
傳給下一個指令,下一個指令是babel()
,就會把你剛讀進來的那些檔案透過 babel compile,再來繼續透過.pipe
傳下去,這次是gulp.dest('dist')
,就是指定輸出的位置叫做dist
這個資料夾,所以你最後的成果就會在這邊被輸出。
如果想重新命名怎麼辦呢?就用一個叫做gulp-rename
的 plugin 就好了:
var gulp = require('gulp');
var babel = require('gulp-babel');
var rename = require("gulp-rename");
gulp.task('default', function() {
gulp.src('my-es6.js')
.pipe(babel())
.pipe(rename('my.js'))
.pipe(gulp.dest('dist'))
});
最後直接來示範一下,用各種 gulp 的 plugin 去打造一個完整的 build 流程
// 這邊就是引入各種 gulp 的 plugin,用法可以自己用關鍵字去查
var gulp = require('gulp');
var babel = require('gulp-babel');
var rename = require("gulp-rename");
var sass = require('gulp-sass');
var gulpSequence = require('gulp-sequence');
var cleanCSS = require('gulp-clean-css');
var uglify = require('gulp-uglify');
// 這個是把 cool.scss compile 成 css,然後寫到 dist 這個資料夾底下
gulp.task('sass', function () {
return gulp.src('cool.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('dist'));
});
// 這個是把 my-es6.js 用 babel compile,重新命名成 my.js 然後存到 dist 底下
gulp.task('babel', function () {
return gulp.src('my-es6.js')
.pipe(babel())
.pipe(rename('my.js'))
.pipe(gulp.dest('dist'));
})
// 這個是壓縮 CSS,並且重新命名叫 cool.min.css
gulp.task('minify-css', function () {
return gulp.src('dist/cool.css')
.pipe(cleanCSS())
.pipe(rename('cool.min.css'))
.pipe(gulp.dest('dist'));
})
// 這個是壓縮 js,重新命名成 my.min.js
gulp.task('uglify-js', function () {
return gulp.src('dist/my.js')
.pipe(uglify())
.pipe(rename('my.min.js'))
.pipe(gulp.dest('dist'));
})
// 這個是為了讓上面的任務有順序的跑
gulp.task('default', gulpSequence('sass', 'babel', 'minify-css', 'uglify-js'));
上面的檔案如果看不懂,你多看幾次找一下規律就會懂了,你會發現怎麼每一個都長得差不多,都是.pipe
然後接一個東西,你要讀檔案就用gulp.src
,要寫檔案就用gulp.dest
,其他事情就去找各種 plugin 用就好了。
用 gulp 的好處就是 plugin 很多,幾乎你想得到的全部都有了。而且我們可以把每個任務都切得很小,方便管理。在最後執行的時候再把每一個任務都串接起來,就是整個 build 的流程了。
我們這一篇之所以最開始先讓你手動跑完這個流程,就是要讓你知道為什麼我們要用 gulp。不是因為潮,是因為每次自己手動 compile 然後貼到壓縮網站上面再自己手動貼上、存檔這個流程實在是太累了,而且很人工,應該要有更好的方式才對。有了 gulp 以後,這種事情都可以寫在 gulp 裡面,你只要簡單地執行一個指令就可以完成所有事項了。
到這一篇為止,你已經可以寫出一個小型的網頁前端專案了,並且利用 gulp 來優化你的 workflow,讓整個編譯的流程體驗更好。前端工程的上半場就到這邊暫時告一個段落了,接下來,要開始後端的部分了!